package com.app.GoCuro.util;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import com.app.GoCuro.data.CurrentSession;
import com.app.GoCuro.data.DatabaseItem;
import com.app.GoCuro.data.InputItem;
import com.app.GoCuro.data.JobsiteInformation;
import com.app.GoCuro.data.LoginResponse;


/*
 * 
 * Methods which provide interface to the database
 * 
 */
public class CustomHttpClient 
{
	//the current context of the application
	Context currContext;
	
	//variables for configuring the http client
	private static HttpClient ourHttpClient;
	private final static int HTTP_TIMEOUT = 30 * 1000;
	
	//common strings for accessing the web client
	private String baseUrl = "http://gocuro.herokuapp.com/";
	private String loginUrl = "login?";
	private	String jobsitesUrl = "jobsites/get_sites";
	private	String getItemsUrl = "items/get_inventory/";
	
	//error strings
	private String loginError = "Invalid login credentials.";
	private String serverConnectionError = "Error connecting to the server.";
	private String databaseConnectionError = "Error parsing out databases";
	private String databaseItemRetrieveError = "Error retrieving items for the database";
	private String connectionError = "Network connectivity not available.";
	private String databasePostError = "Error sending information to the database";
	
	//JSON fields
	private String statusJSON = "status";
	private String jobsiteJSON = "jobsitename";
	private String jobsiteIdJSON = "jobsiteid";
	private String nameJSON = "name";
	private String updatedJSON="updated_at";
	private String idJSON = "id";
	private String barcodeJSON = "barcode_id";
	private String userIdJSON = "userid";
	
	/*
	 * Constructor takes an instance of an activity for testing connectivity and raising dialogs
	 */
	public CustomHttpClient(Context context)
	{
		currContext = context;
	}
	
	/*
	 * Get the instance of the HttpClient, lazily instantiated to create only once
	 * 
	 */
	private static HttpClient getHttpClient()
	{
		//create the client if it does not exist
		if (ourHttpClient == null)
		{
			ourHttpClient = new DefaultHttpClient();
			final HttpParams params = ourHttpClient.getParams();
			HttpConnectionParams.setConnectionTimeout(params, HTTP_TIMEOUT);
			HttpConnectionParams.setSoTimeout(params, HTTP_TIMEOUT);
			ConnManagerParams.setTimeout(params, HTTP_TIMEOUT);
		}
		return ourHttpClient;
	}
	
	/*
	 * Attempts to log into the database from the phone client
	 * 
	 * @param String username	the username to use to login
	 * @param String password	the password for the user for logging in
	 * @return					a login response containing information about the user
	 */
	public LoginResponse login(String username, String password)
	{		
		if (!isOnline()) return null;
		
		HttpClient client = getHttpClient();
		String requestUrl;
		requestUrl = String.format(baseUrl + loginUrl + "username=%s&pwd=%s", username, password);
		HttpPost get = new HttpPost(requestUrl);
		
		try
		{
			HttpResponse response = client.execute(get);
			//act on a valid http response
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
			{
				DataInputStream in = new DataInputStream(response.getEntity().getContent());
				JSONObject retJSON = new JSONObject(in.readLine());
				String JSONstatusCode = retJSON.getString(statusJSON);
				
				if (JSONstatusCode.equals("200"))
				{
					String defaultJobsite = retJSON.getString(jobsiteJSON);
					String jobsiteId = retJSON.getString(jobsiteIdJSON);
					String userId = retJSON.getString(userIdJSON);
					LoginResponse loginResponse = new LoginResponse(defaultJobsite, jobsiteId, username, userId, password);
					CurrentSession currSession = (CurrentSession) currContext.getApplicationContext();
					currSession.setLogin(loginResponse);
					return loginResponse;
				}
				else
				{
					ErrorMessageCreator.CreateErrorMessage(currContext, loginError);
					return null;
				}
			}
			else
			{
				ErrorMessageCreator.CreateErrorMessage(currContext, serverConnectionError);
				return null;
			}
		}
		catch (IOException e)
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, loginError);
			return null;
		}
		catch (JSONException e)
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, loginError);
			return null;
		}
	}
	
	/*
	 * Gets the databases associated with a user
	 * 
	 * @return					a list of objects with information about each database
	 */
	public ArrayList<JobsiteInformation> getUserDatabases()
	{
		if (!isOnline()) return null;
		
		HttpClient client = getHttpClient();
		String requestUrl = baseUrl + jobsitesUrl;
		HttpGet get = new HttpGet(requestUrl);

		try
		{
			HttpResponse response = client.execute(get);
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
			{
				DataInputStream in = new DataInputStream(response.getEntity().getContent());
				String validResponse = in.readLine();
				JSONArray retJSON = new JSONArray(validResponse);
				
				ArrayList<JobsiteInformation> databases = new ArrayList<JobsiteInformation>();
				
				//add the name of databases to the array list
				for (int i = 0; i < retJSON.length(); i++)
				{
					JSONObject element = retJSON.getJSONObject(i);
					String dbName = element.getString(nameJSON);
					String dbId = element.getString(idJSON);
					String lastUpdate = element.getString(updatedJSON);
					
					JobsiteInformation currDatabase = new JobsiteInformation(dbName, dbId, lastUpdate);
					databases.add(currDatabase);
				}
				
				return databases;
			}		
			
		}
		catch (JSONException e)
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, databaseConnectionError);
			return null;
		}
		catch (IOException e)
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, databaseConnectionError);
			return null;
		}

		ErrorMessageCreator.CreateErrorMessage(currContext, databaseConnectionError);
		return null;
	}
	
	/*
	 * gets the items associated with the current database
	 * 
	 * @param String username		the username logging in to get the database
	 * @param String password		the password for the user
	 * @param String databaseId		the id of the database being queried for
	 * 
	 * @return						a list of objects representing items in the database		
	 */
	public ArrayList<DatabaseItem> getDatabaseItems(String username, String password, String databaseId)
	{
		if (!isOnline()) return null;
		
		HttpClient client = getHttpClient();
		String requestUrl = String.format(baseUrl + getItemsUrl + "%s?username=%s&pwd=%s", databaseId, username, password);
		HttpGet get = new HttpGet(requestUrl);
		
		try
		{
			HttpResponse response = client.execute(get);
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
			{
				DataInputStream in = new DataInputStream(response.getEntity().getContent());
				String validResponse = in.readLine();
				JSONArray retJSON = new JSONArray(validResponse);
				
				ArrayList<DatabaseItem> databases = new ArrayList<DatabaseItem>();
				
				//add the name of databases to the array list
				for (int i = 0; i < retJSON.length(); i++)
				{
					JSONObject element = retJSON.getJSONObject(i);
					String barcode = element.getString(barcodeJSON);
					
					DatabaseItem currDatabase = new DatabaseItem(barcode);
					databases.add(currDatabase);
				}
				
				//save the successful login to the global class
				return databases;
			}		
			ErrorMessageCreator.CreateErrorMessage(currContext, databaseItemRetrieveError);
			return null;
		}
		catch (JSONException e)
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, databaseItemRetrieveError);
			return null;
		}
		catch (IOException e)
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, databaseItemRetrieveError);
			return null;
		}		
	}
	
	/*
	 * Posts a new item to the server for use
	 * 
	 * @param ScannedContents contents	the contents of the scan to be transmitted to the server
	 * 
	 * @return true if the post was successful, false if it was not
	 */
	public boolean postNewItem(InputItem enteredItem)
	{
		if (!isOnline()) return false;

		//get the information from the global data store
		CurrentSession session = (CurrentSession) currContext.getApplicationContext();
		LoginResponse currentLogin = session.getLogin();	
		String jobsiteId = session.getJobsite().getId();
		
		HttpClient client = getHttpClient();
		String postUrl = String.format("post/items?username=%s&pwd=%s&quantity=%s&user_id=%s&barcode=%s&jobsite=%s&device_id=%s",
				currentLogin.getUserName(), currentLogin.getPwd(), enteredItem.getQuantity(), currentLogin.getUserId(), 
				enteredItem.getBarcode(), jobsiteId, DeviceIdGenerator.getId(currContext));
		
		HttpPost post = new HttpPost(postUrl);
		
		try
		{
			HttpResponse response = client.execute(post);
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
			{
				return true;
			}		
			ErrorMessageCreator.CreateErrorMessage(currContext, databasePostError);
			return false;
			
		}
		catch (IOException e)
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, databasePostError);
			return false;
		}	
	}
	
	/*
	 * Tests if a connection is available for the application
	 * 
	 * Every connection to the database does this test to ensure 
	 * current operation will send to database
	 * 
	 * @return						true iff connectivity is currently available
	 */
	private boolean isOnline()
	{
		ConnectivityManager cm = (ConnectivityManager) currContext.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo netInfo = cm.getActiveNetworkInfo();
		
		if (netInfo == null || !netInfo.isConnected())
		{
			ErrorMessageCreator.CreateErrorMessage(currContext, connectionError);
			return false;
		}
		return true;
	}
}